// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/renderer/media/media_stream_audio_level_calculator.h"

#include <cmath>
#include <limits>

#include "base/logging.h"
#include "base/stl_util.h"
#include "media/base/audio_bus.h"

namespace content {

namespace {

    // Calculates the maximum absolute amplitude of the audio data.
    float MaxAmplitude(const float* audio_data, int length)
    {
        float max = 0.0f;
        for (int i = 0; i < length; ++i) {
            const float absolute = fabsf(audio_data[i]);
            if (absolute > max)
                max = absolute;
        }
        DCHECK(std::isfinite(max));
        return max;
    }

} // namespace

MediaStreamAudioLevelCalculator::Level::Level()
    : level_(0.0f)
{
}

MediaStreamAudioLevelCalculator::Level::~Level() { }

float MediaStreamAudioLevelCalculator::Level::GetCurrent() const
{
    base::AutoLock auto_lock(lock_);
    return level_;
}

void MediaStreamAudioLevelCalculator::Level::Set(float level)
{
    base::AutoLock auto_lock(lock_);
    level_ = level;
}

MediaStreamAudioLevelCalculator::MediaStreamAudioLevelCalculator()
    : counter_(0)
    , max_amplitude_(0.0f)
    , level_(new Level())
{
}

MediaStreamAudioLevelCalculator::~MediaStreamAudioLevelCalculator()
{
    level_->Set(0.0f);
}

void MediaStreamAudioLevelCalculator::Calculate(
    const media::AudioBus& audio_bus,
    bool assume_nonzero_energy)
{
    // |level_| is updated every 10 callbacks. For the case where callback comes
    // every 10ms, |level_| will be updated approximately every 100ms.
    static const int kUpdateFrequency = 10;

    float max = assume_nonzero_energy ? 1.0f / std::numeric_limits<int16_t>::max() : 0.0f;
    for (int i = 0; i < audio_bus.channels(); ++i) {
        const float max_this_channel = MaxAmplitude(audio_bus.channel(i), audio_bus.frames());
        if (max_this_channel > max)
            max = max_this_channel;
    }
    max_amplitude_ = std::max(max_amplitude_, max);

    if (counter_++ == kUpdateFrequency) {
        // Clip the exposed signal level to make sure it is in the range [0.0,1.0].
        level_->Set(std::min(1.0f, max_amplitude_));

        // Decay the absolute maximum amplitude by 1/4.
        max_amplitude_ /= 4.0f;

        // Reset the counter.
        counter_ = 0;
    }
}

} // namespace content
